package com.hero.tweaks.util;

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashMap;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.AssetManager;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.util.Log;

public class Helpers implements Constants {

	private static String mVoltagePath;
	private static Helpers instance;

	public static String binExist(String b) {
		CMDProcessor.CommandResult cr = null;
		cr = new CMDProcessor().sh.runWaitFor("busybox which " + b);
		if (cr.success()) {
			return cr.stdout;
		} else {
			return NOT_FOUND;
		}
	}

	public static String bln_path() {
		if (new File("/sys/class/misc/backlightnotification/enabled").exists()) {
			return "/sys/class/misc/backlightnotification/enabled";
		} else if (new File("/sys/class/leds/button-backlight/blink_buttons")
				.exists()) {
			return "/sys/class/leds/button-backlight/blink_buttons";
		} else {
			return null;
		}
	}

	public static String buildprop_path() {
		if (new File("/system/build.prop").exists()) {
			return "/system/build.prop";
		} else {
			return null;
		}

	}

	/**
	 * Checks to see if Busybox is installed in "/system/"
	 * 
	 * @return If busybox exists
	 */
	public static boolean checkBusybox() {
		if (!new File("/system/bin/busybox").exists()
				&& !new File("/system/xbin/busybox").exists()) {
			Log.e(TAG, "Busybox not in xbin or bin!");
			return false;
		}
		try {
			if (!new CMDProcessor().su.runWaitFor("busybox mount").success()) {
				Log.e(TAG, " Busybox is there but it is borked! ");
				return false;
			}
		} catch (final NullPointerException e) {
			Log.e(TAG, e.getMessage());
			return false;
		}
		return true;
	}

	/**
	 * Checks device for SuperUser permission
	 * 
	 * @return If SU was granted or denied
	 */
	public static boolean checkSu() {
		if (!new File("/system/bin/su").exists()
				&& !new File("/system/xbin/su").exists()) {
			Log.e(TAG, "su does not exist!!!");
			return false; // tell caller to bail...
		}
		try {
			if ((new CMDProcessor().su.runWaitFor("ls /data/app-private"))
					.success()) {
				Log.i(TAG, " SU exists and we have permission");
				return true;
			} else {
				Log.i(TAG, " SU exists but we dont have permission");
				return false;
			}
		} catch (final NullPointerException e) {
			Log.e(TAG, e.getMessage());
			return false;
		}
	}

	public static String dt2w_path() {
		if (new File("/sys/android_touch/doubletap2wake").exists()) {
			return "/sys/android_touch/doubletap2wake";
		} else {
			return null;
		}
	}

	public static String f2s_path() {
		if (new File("/sys/devices/virtual/htc_g_sensor/g_sensor/flick2sleep")
				.exists()) {
			return "/sys/devices/virtual/htc_g_sensor/g_sensor/flick2sleep";
		} else {
			return null;
		}
	}

	public static String f2w_path() {
		if (new File("/sys/devices/virtual/htc_g_sensor/g_sensor/flick2wake")
				.exists()) {
			return "/sys/devices/virtual/htc_g_sensor/g_sensor/flick2wake";
		} else {
			return null;
		}
	}

	public static String fastcharge_path() {
		if (new File("/sys/kernel/fast_charge/force_fast_charge").exists()) {
			return "/sys/kernel/fast_charge/force_fast_charge";
		} else if (new File("/sys/module/msm_otg/parameters/fast_charge")
				.exists()) {
			return "/sys/module/msm_otg/parameters/fast_charge";
		} else if (new File("/sys/devices/platform/htc_battery/fast_charge")
				.exists()) {
			return "/sys/devices/platform/htc_battery/fast_charge";
		} else {
			return null;
		}
	}

	public static void get_assetsScript(String fn, Context c, String prefix,
			String postfix) {
		byte[] buffer;
		final AssetManager assetManager = c.getAssets();
		try {
			InputStream f = assetManager.open(fn);
			buffer = new byte[f.available()];
			f.read(buffer);
			f.close();
			final String s = new String(buffer);
			final StringBuffer sb = new StringBuffer(s);
			if (!postfix.equals("")) {
				sb.append("\n\n" + postfix);
			}
			if (!prefix.equals("")) {
				sb.insert(0, prefix + "\n");
			}
			sb.insert(0, "#!" + Helpers.binExist("sh") + "\n\n");
			try {
				FileOutputStream fos;
				fos = c.openFileOutput(fn, Context.MODE_PRIVATE);
				fos.write(sb.toString().getBytes());
				fos.close();

			} catch (IOException e) {
				Log.d(TAG, "error write " + fn + " file");
				e.printStackTrace();
			}

		} catch (IOException e) {
			Log.d(TAG, "error read " + fn + " file");
			e.printStackTrace();
		}
	}

	public static String[] getAvailableIOSchedulers() {
		String[] schedulers = null;
		String[] aux = readStringArray(IO_SCHEDULER_PATH);
		if (aux != null) {
			schedulers = new String[aux.length];
			for (byte i = 0; i < aux.length; i++) {
				if (aux[i].charAt(0) == '[') {
					schedulers[i] = aux[i].substring(1, aux[i].length() - 1);
				} else {
					schedulers[i] = aux[i];
				}
			}
		}
		return schedulers;
	}

	/**
	 * Helper to update the app widget
	 * 
	 * @param context
	 */
	public static Bitmap getBackground(int bgcolor) {
		try {
			Bitmap.Config config = Bitmap.Config.ARGB_8888;
			Bitmap bitmap = Bitmap.createBitmap(2, 2, config);
			Canvas canvas = new Canvas(bitmap);
			canvas.drawColor(bgcolor);
			return bitmap;
		} catch (Exception e) {
			return null;
		}
	}

	public static String getCachePartition() {
		CMDProcessor.CommandResult cr = null;
		cr = new CMDProcessor().sh
				.runWaitFor("busybox echo `busybox mount | busybox grep cache | busybox cut -d' ' -f1`");
		if (cr.success() && !cr.stdout.equals("")) {
			return cr.stdout;
		} else {
			return NOT_FOUND;
		}
	}

	public static Helpers getInstance() {
		if (instance == null)
			return new Helpers();
		else
			return instance;
	}

	public static String getIOScheduler() {
		String scheduler = null;
		String[] schedulers = readStringArray(IO_SCHEDULER_PATH);
		if (schedulers != null) {
			for (String s : schedulers) {
				if (s.charAt(0) == '[') {
					scheduler = s.substring(1, s.length() - 1);
					break;
				}
			}
		}
		return scheduler;
	}

	/**
	 * Get mounts
	 * 
	 * @param mount
	 * @return success or failure
	 */
	public static boolean getMount(final String mount) {
		final CMDProcessor cmd = new CMDProcessor();
		final String mounts[] = getMounts("/system");
		if (mounts != null && mounts.length >= 3) {
			final String device = mounts[0];
			final String path = mounts[1];
			final String point = mounts[2];
			if (cmd.su.runWaitFor(
					"mount -o " + mount + ",remount -t " + point + " " + device
							+ " " + path).success()) {
				return true;
			}
		}
		return (cmd.su.runWaitFor("busybox mount -o remount," + mount
				+ " /system").success());
	}

	/**
	 * Return mount points
	 * 
	 * @param path
	 * @return line if present
	 */
	public static String[] getMounts(final String path) {
		try {
			BufferedReader br = new BufferedReader(new FileReader(
					"/proc/mounts"), 256);
			String line = null;
			while ((line = br.readLine()) != null) {
				if (line.contains(path)) {
					return line.split(" ");
				}
			}
			br.close();
		} catch (FileNotFoundException e) {
			Log.d(TAG, "/proc/mounts does not exist");
		} catch (IOException e) {
			Log.d(TAG, "Error reading /proc/mounts");
		}
		return null;
	}

	public static int getNumOfCpus() {
		int numOfCpu = 1;
		String numOfCpus = Helpers.readOneLine(NUM_OF_CPUS_PATH);
		String[] cpuCount = numOfCpus.split("-");
		if (cpuCount.length > 1) {
			try {
				int cpuStart = Integer.parseInt(cpuCount[0]);
				int cpuEnd = Integer.parseInt(cpuCount[1]);

				numOfCpu = cpuEnd - cpuStart + 1;

				if (numOfCpu < 0)
					numOfCpu = 1;
			} catch (NumberFormatException ex) {
				numOfCpu = 1;
			}
		}
		return numOfCpu;
	}

	public static String getProp(String prop) {
		try {
			Process process = Runtime.getRuntime().exec("getprop " + prop);
			BufferedReader bufferedReader = new BufferedReader(
					new InputStreamReader(process.getInputStream()));
			StringBuilder log = new StringBuilder();
			String line;
			while ((line = bufferedReader.readLine()) != null) {
				log.append(line);
			}
			return log.toString();
		} catch (IOException e) {
			// Runtime error
		}
		return null;
	}

	public static String getROMVersion() {
		Helpers.getInstance();
		String romVersion = Helpers.getProp("ro.rom.id");
		if (romVersion != null)
			return romVersion;
		else
			return "<none>";
	}

	/**
	 * Gets the currently set voltage path
	 * 
	 * @return voltage path
	 */
	public static String getVoltagePath() {
		return mVoltagePath;
	}

	/*
	 * @return available performance scheduler
	 */
	public static Boolean GovernorExist(String gov) {
		if (readOneLine(GOVERNORS_LIST_PATH).indexOf(gov) > -1) {
			return true;
		} else {
			return false;
		}
	}

	public static String initd_path() {
		if (new File("/system/etc/init.d/initd").exists()) {
			return "/system/etc/init.d/initd";
		} else {
			return null;
		}

	}

	public static void initGodMode() {
		CMDProcessor.startSuCommand("chmod -r 777 /system/");
	}

	public static String l2m_path() {
		if (new File("/sys/android_touch/logo2menu").exists()) {
			return "/sys/android_touch/logo2menu";
		} else {
			return null;
		}
	}

	public static Boolean moduleActive(String b) {
		CMDProcessor.CommandResult cr = null;
		cr = new CMDProcessor().sh
				.runWaitFor("busybox echo `busybox ps | busybox grep " + b
						+ " | busybox grep -v \"busybox grep " + b
						+ "\" | busybox awk '{print $1}'`");
		Log.d(TAG, "Module: " + cr.stdout);
		return cr.success() && !cr.stdout.equals("");
	}

	public static String p2s_path() {
		if (new File("/sys/devices/virtual/htc_g_sensor/g_sensor/pick2sleep")
				.exists()) {
			return "/sys/devices/virtual/htc_g_sensor/g_sensor/pick2sleep";
		} else {
			return null;
		}
	}

	public static String p2w_path() {
		if (new File("/sys/devices/virtual/htc_g_sensor/g_sensor/pick2wake")
				.exists()) {
			return "/sys/devices/virtual/htc_g_sensor/g_sensor/pick2wake";
		} else {
			return null;
		}
	}

	public static String pd_path() {
		if (new File("/sys/android_touch/pocket_detect").exists()) {
			return "/sys/android_touch/pocket_detect";
		} else {
			return null;
		}
	}

	public static String ReadableByteCount(long bytes) {
		int unit = 1024;
		if (bytes < unit)
			return bytes + " B";
		int exp = (int) (Math.log(bytes) / Math.log(unit));
		String pre = String.valueOf("KMGTPE".charAt(exp - 1));
		return String.format("%.1f %sB", bytes / Math.pow(unit, exp), pre);
	}

	public static String readCPU(Context context, int i) {
		Helpers.get_assetsScript("utils", context, "", "");
		new CMDProcessor().sh.runWaitFor("busybox chmod 750 "
				+ context.getFilesDir() + "/utils");
		CMDProcessor.CommandResult cr = new CMDProcessor().su
				.runWaitFor(context.getFilesDir() + "/utils -getcpu " + i);
		if (cr.success())
			return cr.stdout;
		else
			return null;
	}

	// Check to see if Axis ROM is installed

	/**
	 * Read file via shell
	 * 
	 * @param filePath
	 * @param useSu
	 * @return file output
	 */
	public static String readFileViaShell(String filePath, boolean useSu) {
		CMDProcessor.CommandResult cr = null;
		if (useSu) {
			cr = new CMDProcessor().su.runWaitFor("cat " + filePath);
		} else {
			cr = new CMDProcessor().sh.runWaitFor("cat " + filePath);
		}
		if (cr.success())
			return cr.stdout;
		return null;
	}

	public static String readOneLine(String fname) {
		String line = null;
		if (new File(fname).exists()) {
			BufferedReader br;
			try {
				br = new BufferedReader(new FileReader(fname), 512);
				try {
					line = br.readLine();
				} finally {
					br.close();
				}
			} catch (Exception e) {
				// Log.e(TAG, "IO Exception when reading sys file", e);
				// attempt to do magic!
				return readFileViaShell(fname, true);
			}
		}
		return line;
	}

	/**
	 * Reads string array from file
	 * 
	 * @param fname
	 * @return string array
	 */
	private static String[] readStringArray(String fname) {
		String line = readOneLine(fname);
		if (line != null) {
			return line.split(" ");
		}
		return null;
	}

	public static void restartHotreboot() {
		CMDProcessor.startSuCommand("setprop ctl.restart zygote");
	}

	/**
	 * Restart the activity smoothly
	 * 
	 * @param activity
	 */
	public static void restartPC(final Activity activity) {
		if (activity == null)
			return;
		final int enter_anim = android.R.anim.fade_in;
		final int exit_anim = android.R.anim.fade_out;
		activity.overridePendingTransition(enter_anim, exit_anim);
		activity.finish();
		activity.overridePendingTransition(enter_anim, exit_anim);
		activity.startActivity(activity.getIntent());
	}

	public static void restartSystemUI() {
		CMDProcessor.startSuCommand("pkill -TERM -f com.android.systemui");
	}

	public static void restartPrism() {
		CMDProcessor.startSuCommand("pkill -TERM -f com.htc.launcher");
	}

	public static String s2w_path() {
		if (new File("/sys/android_touch/sweep2wake").exists()) {
			return "/sys/android_touch/sweep2wake";
		} else {
			return null;
		}
	}

	/**
	 * Sets the voltage file to be used by the rest of the app elsewhere.
	 * 
	 * @param voltageFile
	 */
	public static void setVoltagePath(String voltageFile) {
		Log.d(TAG, "UV table path detected: " + voltageFile);
		mVoltagePath = voltageFile;
	}

	public static String shExec(StringBuilder s) {
		if (new File(SH_PATH).exists()) {
			s.insert(0, "#!" + binExist("sh") + "\n\n");
			new CMDProcessor().su.runWaitFor("busybox echo \"" + s.toString()
					+ "\" > " + SH_PATH);
			new CMDProcessor().su.runWaitFor("busybox chmod 755 " + SH_PATH);
			CMDProcessor.CommandResult cr = null;
			cr = new CMDProcessor().su.runWaitFor(SH_PATH);
			if (cr.success()) {
				return cr.stdout;
			} else {
				Log.d(TAG, "execute: " + cr.stderr);
				return "";
			}
		} else {
			Log.d(TAG, "missing file: " + SH_PATH);
			return "";
		}
	}

	public static String shExec(StringBuilder s, Context c, Boolean su) {
		get_assetsScript("run", c, "", s.toString());
		new CMDProcessor().sh.runWaitFor("busybox chmod 750 " + c.getFilesDir()
				+ "/run");
		CMDProcessor.CommandResult cr = null;
		if (su)
			cr = new CMDProcessor().su.runWaitFor(c.getFilesDir() + "/run");
		else
			cr = new CMDProcessor().sh.runWaitFor(c.getFilesDir() + "/run");
		if (cr.success()) {
			return cr.stdout;
		} else {
			Log.d(TAG, "execute: " + cr.stderr);
			return null;
		}
	}

	public static boolean showBattery() {
		return ((new File(BLX_PATH).exists()) || (fastcharge_path() != null));
	}

	public static void shWrite(String f) {
		new CMDProcessor().su.runWaitFor("busybox cat " + f + " > " + SH_PATH);
		new CMDProcessor().su.runWaitFor("busybox chmod 755 " + SH_PATH);
	}

	public static void toggleAppIcon(Context context, boolean newState) {
		ComponentName componentName = new ComponentName("com.hero.tweaks",
				"com.hero.tweaks.ui.activities.MainActivity");
		context.getPackageManager().setComponentEnabledSetting(
				componentName,
				newState ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
						: PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
				PackageManager.DONT_KILL_APP);
	}

	/**
	 * Convert to MHz and append a tag
	 * 
	 * @param mhzString
	 * @return tagged and converted String
	 */
	public static String toMHz(String mhzString) {
		if (mhzString == null)
			return "";
		else
			return String.valueOf(Integer.parseInt(mhzString) / 1000) + " MHz";
	}

	/**
	 * Check if any voltage control tables exist and set the voltage path if a
	 * file is found.
	 * <p/>
	 * If false is returned, there was no tables found and none will be used.
	 * 
	 * @return true/false if uv table exists
	 */
	public static boolean voltageFileExists() {
		if (new File(UV_MV_PATH).exists()) {
			setVoltagePath(UV_MV_PATH);
			return true;
		} else if (new File(VDD_PATH).exists()) {
			setVoltagePath(VDD_PATH);
			return true;
		} else if (new File(VDD_SYSFS_PATH).exists()) {
			setVoltagePath(VDD_SYSFS_PATH);
			return true;
		} else if (new File(COMMON_VDD_PATH).exists()) {
			setVoltagePath(COMMON_VDD_PATH);
			return true;
		}
		return false;
	}

	public static String vsync_path() {
		if (new File("/sys/kernel/debug/msm_fb/0/vsync_enable").exists()) {
			return "/sys/kernel/debug/msm_fb/0/vsync_enable";
		} else {
			return null;
		}

	}

	/**
	 * Write one line to a file
	 * 
	 * @param fname
	 * @param value
	 * @return if line was written
	 */
	public static boolean writeOneLine(String fname, String value) {
		if (!new File(fname).exists()) {
			return false;
		}
		try {
			FileWriter fw = new FileWriter(fname);
			try {
				fw.write(value);
			} finally {
				fw.close();
			}
		} catch (IOException e) {
			String Error = "Error writing to " + fname + ". Exception: ";
			Log.e(TAG, Error, e);
			return false;
		}
		return true;
	}

	HashMap<String, String> props;

	public Helpers() {
		readBuildProp();
	}

	private void readBuildProp() {
		props = new HashMap<String, String>();
		try {
			FileInputStream fstream = new FileInputStream("/system/build.prop");
			DataInputStream in = new DataInputStream(fstream);
			BufferedReader br = new BufferedReader(new InputStreamReader(in));
			String strLine;
			while ((strLine = br.readLine()) != null) {
				// System.out.println(strLine);
				String[] line = strLine.split("=");
				if (line.length > 1)
					props.put(line[0], line[1]);
			}
			in.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}
